#pragma once

#include "Sol9Types.h"
#include "Sol9Math.h"
#include "Sol9Util.h"

namespace sol9
{
namespace core
{

	template <typename value>
	class vector2d;

	template <typename value>
	class dimension2d
	{
	public:
		value width, height;

	public:
		// constructors
		dimension2d() : width(0), height(0) {}

		dimension2d(value _width, value _height) : width(_width), height(_height) {}

		dimension2d(const vector2d<value>& other); // defined in vector2d.h

		template <typename value2>
		explicit dimension2d(const dimension2d<value2>& other)
			: width((value)other.width), height((value)other.height) {}

		// operators
		// -dimension
		dimension2d<value> operator-() const
		{
			return dimension2d<value>(-width, -height);
		}

		// dimension = dimension
		template <typename value2>
		dimension2d<value>& operator=(const dimension2d<value2>& other)
		{
			width = (value)other.widht;
			height = (value)other.height;
			return *this;
		}

		// dimension + dimension
		dimension2d<value> operator+(const dimension2d<value>& other) const
		{
			return dimension2d<value>(width + other.width, height + other.height);
		}

		// dimension += dimension
		dimension2d<value>& operator+=(const dimension2d<value>& other)
		{
			width += other.width;
			height += other.height;
			return *this;
		}

		// dimension - dimension
		dimension2d<value> operator-(const dimension2d<value>& other) const
		{
			return dimension2d<value>(width - other.width, height - other.height);
		}

		// dimension -= dimension
		dimension2d<value>& operator-=(const dimension2d<value>& other)
		{
			width -= other.width;
			height -= other.height;
			return *this;
		}

		// dimension * scalar
		dimension2d<value> operator*(const value scalar) const
		{
			return dimension2d<value>(width * scalar, height * scalar);
		}

		// dimension *= scalar
		dimension2d<value>& operator*=(const value scalar)
		{
			width *= scalar;
			height *= scalar;
			return *this;
		}

		// dimension / scalar
		dimension2d<value> operator/(const value scalar) const
		{
			if (core::isZero(scalar))
				return this;

			return dimension2d<value>(width / scalar, height / scalar);
		}

		// dimension /= scalar
		dimension2d<value>& operator/=(const value scalar)
		{
			if (core::isZero(scalar))
				return *this;

			width /= scalar;
			height /= scalar;
			return *this;
		}

		// dimension == dimension
		bool operator==(const dimension2d<value>& other) const
		{
			return core::equals(width, other.width)
				&& core::equals(height, other.height);
		}

		// dimension != dimension
		bool operator!=(const dimension2d<value>& other) const
		{
			return !(*this == other);
		}
		
		// dimension == vector
		bool operator==(const vector2d<value>& other) const; // defined in vector2d.h

		// dimension != vector
		bool operator!=(const vector2d<value>& other) const
		{
			return !(*this == other);
		}

		// functions
		dimension2d<value>& set(const value _width, const value _height)
		{
			width = _width;
			height = _height;
			return *this;
		}

		template <typename value2>
		dimension2d<value>& set(const dimension2d<value2>& other)
		{
			width = (value)other.width;
			height = (value)other.height;
			return *this;
		}

		value getArea() const
		{
			return width * height;
		}
	};

	typedef dimension2d<f32> dimension2df;
	typedef dimension2d<u32> dimension2du;

	/** There are few cases where negative dimesions make sense
		Please consider using dimension2du instead. */
	typedef dimension2d<s32> dimension2di;

} // namespace core
} // namespace sol9